 
#ifndef __rsp_tile__HEADER_FILE__
#define __rsp_tile__HEADER_FILE__

#include "rsp_def.h"
#include "update.h"

using namespace coid;

/**
    Tracking the replication set.
    -----------------------------

    For every populated tile, compute the command diff buffer, and mark newly
    introduced objects, alongwith their source tile.
    Group viewers by tile; viewers from the same tile would receive the same
    update message, so it should be computed just once.
    For every populated tile, get the set of visible tiles from it. Assemble the
    update buffers from these files to one message, and send the message to all
    viewers from the primary tile. At the beginning of the message should be
    a full update of objects that came from outside of the visible tile set. It's
    computed by taking the marked newcomer objects of tile and checking if their
    source tile is within the visible tile set.

    Object-specific part of the update generation process takes care of having all
    the objects referenced by the visible tiles instantiated on the client first.
    So there can be two types of updates - for newly connected viewers, all the visible
    objects are replicated. This can be done incrementally, by extending the visibility
    radius gradually for newly connected viewer, just after it had successfully
    synchronized objects from the previous radius.
    For existing objects, and the tiles they belong to, a common update is created that
    encompasses full updates for newly visible objects. That means that the visibility
    set is computed with the tile granularity.

    Objects that just crossed a tile boundary, so that in the previous frame they
    belonged to one tile and now to another, may not have all required visible objects
    instantiated. They must be treated specially - it's required that they get full
    updates of objects not visible before. After that part of the update they share the
    update block for all objects in the existing tile.

    Tile keeps a newcomers list, where all objects that moved into the tile are inserted
    by the physics in the previous frame. The replication process begins by computing
    the command update buffer for each populated tile. For this it takes all listed
    objects, including the newcomers, and generates the updates in the form
    oid(4B)+cmd(4B) but only if the cmd isn't null (no commands).

    The next step is to compute a list of objects that have to be replicated fully because
    they weren't visible from a given tile in the previous frame. These are all newcomer
    objects from the tiles at the perimeter of visibility from given tile. A concatenated
    update buffer from all the objects is created and kept at the tile, to be sent with
    the differential data to each tile object.
    The layout of the update buffer would then be:
     <command buffers> <full updates for newly visible> [<full updates for moved objects>]
    
    The last part will be written only for objects that just moved to the tile from another
    one. It would contain full updates for objects that appear on newly visible tiles.


    Format of the frame command buffer.
    -----------------------------------

    The frame command buffer (FCB) holds commands for objects that live on the tile at
    the specific time (frame). Finalized FCB's are sent to all connected tile viewers.
    The FCB contains a chain of entries with object commands, with each entry composed of
    object id followed by commands for the object.
        Unfinalized FCB's (of leading frames that weren't yet commited by the server) can be
    updated either by appending new commands for remaining objects, or by removing entries
    for objects that moved to another tile by finalization of previous frames.
    It's not possible to rewrite commands by the client, so no editing of commands in FCB
    is allowed. Deletion can be performed by setting a flag that marks the entry invalid,
    or if the entry is too long it can be removed completely.
        For this to be effective, each object should contain pointer to the FCB where its
    commands are stored, for each leading frame, or NULL if it's got no commands in there.
    This is then used for deletions and for verification that the object already doesn't
    contain commands for the frame. During the finalization of frame, the FCB is first 
    traversed and commands are executed on their respective objects. Frame completion methods
    are then executed on all tile objects; this may cause that the object is moved to a
    neighbour tile, and that the leading frames should be checked and their FCB's updated
    to reflect the change.
    

**/


////////////////////////////////////////////////////////////////////////////////
///Spatial block managing group of objects
struct rsp_tile
{
    RSP_CLASS_NAME("t",rsp_tile)


    ///Write command buffer for specified object and leading frame
    void write_command( rsp_base* obj, int leadfr, const void* data, ushort len )
    {
        frame_command_buffer& fcb = fcbs[ RSP_SETTINGS.active_slot(leadfr) ];
        uchar* p = fcb.add( sizeof(uint) + sizeof(ushort) + len );

        //store 4B oid, 2B command size and the command data
        *(uint*)p = obj->oid();
        p += sizeof(uint);
        *(ushort*)p = len;
        p += sizeof(ushort);
        ::memcpy( p, data, len );
    }


    void frame()
    {
        //finalization of current FCB
        frame_command_buffer& fcb = fcbs[ RSP_SETTINGS.active_slot() ];

        //execute object commands
        const uchar* pd = (const uchar*)fcb.ptr();
        const uchar* pe = pd + fcb.size();
        for( ; pd<pe; )
        {
            uint oid = *(const uint*)pd;
            rsp_base* bs = rsp_base::from_oid(oid);
            pd += sizeof(uint);
            ushort len = *(const ushort*)pd;
            pd += sizeof(ushort);
            bs->exec_commands( pd, pd+len );
            pd += len;
        }

        //process the oldies
        rsp_base** bsp = &living;
        rsp_base* bs = living;
        while(bs)
        {
            rsp_tile* dst = bs->frame();
            if(dst) //moved away
            {
                *bsp = bs->next;
                dst->add_newcomer(bs);
            }
            bsp = &bs->next;
            bs = bs->next;
        }

    }

    rsp_tile()
    {
    }


protected:

    /*@{
        Tile has to deal with objects that moved to it from the neighbor tiles. These could move to
        the file before or after it was processed in the frame() method. Newcomers are needed in the
        next pass, but it's required to have a list of them because during the replication the
        algorithm needs to traverse the newcomers list because a full replication of a newcomer might
        be required.
    **/
    rsp_base* living;                           ///< ptr to the first object in the stable list
    rsp_base* newcome;                          ///< ptr to the first newcomer object
    //@}

    ///Initialize tile lists
    void list_init()                            { living = newcome = 0; }

    ///Reset for the next frame (no newcomers)
    void list_reset()
    {
        //append living to newcome
        rsp_base** next = &newcome;
        while(*next)  next = &(*next)->next;

        *next = living;
        living = newcome;
        newcome = 0;
    }

    void add_newcomer( rsp_base* obj )
    {
        obj->next = newcome;
        newcome = obj;
    }

    ///Array of frame command buffers for trailing and leading frames
    dynarray<frame_command_buffer> fcbs;
};




#endif //__rsp_tile__HEADER_FILE__
